
#include "Mesh.h"
#include "Surfaces.h"
#include "OgreMath/OgreVector4.h"

TransformedMesh::TransformedMesh(std::shared_ptr<Mesh> mesh)
	: mMesh(mesh), mPosition(Ogre::Vector3(0,0,0)), mScale(Ogre::Vector3(1,1,1)), mRotation(Ogre::Matrix3::IDENTITY), vertexBufferVS(mesh->vertexBuffer)
{
	// create triangle objects for BVH etc
	triangleSurfaces.clear();
	triangleSurfaces.reserve(mesh->indexBuffer.size() / 3);
	for (unsigned int i = 0; i < mMesh->indexBuffer.size() / 3; i++)
		triangleSurfaces.push_back(TriangleSurface(this, i));
}

void TransformedMesh::applyModelView(const Ogre::Matrix4 &viewMatrix) const
{
	Ogre::Matrix4 modelTransform, modelTransformNoScale;
	modelTransform.makeTransform(mPosition, mScale, mRotation);
	modelTransformNoScale.makeTransform(mPosition, Ogre::Vector3(1,1,1), mRotation);	// for normal transformation
	Ogre::Matrix4 modelViewMatrix = viewMatrix * modelTransform;
	Ogre::Matrix4 modelViewMatrixNoScale = viewMatrix * modelTransformNoScale;

	// not exactly elegant use of homogenous notation but after all it's slightly more efficient to multiply the normals with a 3x3 matrix
	Ogre::Matrix3 normalTransform;
	modelViewMatrixNoScale.extract3x3Matrix(normalTransform);

	// transform vertices with modelViewMatrix which includes translation, rotation and scale
	vertexBufferVS.clear();
	vertexBufferVS.resize(mMesh->vertexBuffer.size());
	auto it = vertexBufferVS.begin();
	for (auto i = mMesh->vertexBuffer.begin(); i != mMesh->vertexBuffer.end(); ++i)
	{
		it->position = modelViewMatrix * i->position;
		//apply only the rotation to the normal
		it->normal = normalTransform * i->normal;
		it->normal.normalise();
		++it;
	}

	triangleDataVS.clear();
	triangleDataVS.resize(mMesh->triangleNormals.size());
	int triIndex = 0;
	int numDegeneratedTris = 0;
	for (unsigned int index = 0; index < mMesh->indexBuffer.size(); index += 3)
	{
		Ogre::Vector3 p1 = vertexBufferVS[mMesh->indexBuffer[index]].position;
		Ogre::Vector3 p2 = vertexBufferVS[mMesh->indexBuffer[index+1]].position;
		Ogre::Vector3 p3 = vertexBufferVS[mMesh->indexBuffer[index+2]].position;

		Ogre::Vector3 v0 = p2 - p1;
		Ogre::Vector3 v1 = p3 - p1;

		// recompute triangle normals for maximal accuracy
		RayTriangleIntersectionData &triData = triangleDataVS[triIndex++];
		triData.normal = v0.crossProduct(v1);
		triData.normal.normalise();

		// slightly inspired by http://software.intel.com/en-us/articles/interactive-ray-tracing
		// determine dominant axis
		int domAxis = 0;
		if (std::abs(triData.normal.y) >= std::abs(triData.normal.x) && std::abs(triData.normal.y) >= std::abs(triData.normal.z))
			domAxis = 1;
		else if (std::abs(triData.normal.z) >= std::abs(triData.normal.x) && std::abs(triData.normal.z) >= std::abs(triData.normal.y))
			domAxis = 2;
		triData.equationIndex1 = (domAxis+1) % 3;
		triData.equationIndex2 = (domAxis+2) % 3;

		double a = p2[triData.equationIndex1]-p1[triData.equationIndex1];
		double b = p3[triData.equationIndex1]-p1[triData.equationIndex1];
		double c = p2[triData.equationIndex2]-p1[triData.equationIndex2];
		double d = p3[triData.equationIndex2]-p1[triData.equationIndex2];
		double denom = a*d - b*c;
		if (std::abs(denom) < std::numeric_limits<double>::epsilon())
		{
			numDegeneratedTris++;
			//std::cout << "Degenerated triangle: " << p1 << " " << p2 << " " << p3 << " denom: " << denom << std::endl;
		}
		triData.aInv = static_cast<float>(d/denom);
		triData.bInv = static_cast<float>(-b/denom);
		triData.cInv = static_cast<float>(-c/denom);
		triData.dInv = static_cast<float>(a/denom);
	}
	std::cout << "Found " << numDegeneratedTris << " degenerated triangles." << std::endl;

	for (auto i = triangleSurfaces.begin(); i != triangleSurfaces.end(); i++)
		i->updateBoundingVolume();
}